from helpFunc import *
import socket,select,os
import time,struct
from base import *
import threading,queue
recv_q = queue.Queue()
port_g = 0
key_g = b''
lock = threading.Lock()   
one_g = ('',-1,-1,-1,-1)
import hashlib  
        
class scapyRec():
    def __init__(self,n):      
        self.sock = s = socket.socket()
        add = ('127.0.0.1',n)
        s.connect(add)
        print ('scapyRec ok')  
        self.updateTime=0
        self.portMap = {}
        self.key = ('',-1)   
        self.lastpeerKey = ('',-1)
        self.staTime = time.monotonic()
        a = sniff(prn=self.aa, filter="tcp and port %s"%(port_g) ,store=0,iface=con_iface)
        
    def refresh(self):        
        for one in sorted(list(self.portMap.keys())):
            v = self.portMap[one]
            if v['updateTime']+1000<time.monotonic():
                del self.portMap[one]
                if self.key == one:
                    self.key = ('',-1)   
                    print ('key change',-1)
                continue
         
    def aa(self,p):
        global one_g
        t = p[TCP]
        i = p[IP]
        p = t.payload.original   
        dk = hashlib.pbkdf2_hmac('md5', p[:-16], con_key, 2)    
        isMy = p[-16:]== dk                
        if isMy and t.dport==port_g and p[:6]==key_g:
            if (self.key != (i.src,t.sport) or self.updateTime+0.1<time.monotonic()) :
                self.updateTime = time.monotonic()
                if self.key != (i.src,t.sport):
                    print ('key change',(i.src,t.sport))
                self.key =  (i.src,t.sport)                
                se = t.ack-2000
                if se<0:
                    se += 0xffffffff 
                ack = t.seq+2000
                lock.acquire() 
                one_g=(self.key[0],self.key[1],se,ack,500)                     
                lock.release()   
            if recv_q.qsize()>50:
                return                
            recv_q.put(p[6:-16])
            self.sock.sendall(b'1')
            return 
        if isMy:
            return           
  
class ser(base):
    def __init__(self,dataPort,numPort,onePakcLen,totalSendLen,
                 packEffectTime,timeoutInter,port,k):
        super().__init__(dataPort,numPort,onePakcLen,packEffectTime,timeoutInter)
        global port_g,key_g
        port_g = port     
        key_g = k     
        self.staTime = time.monotonic()
        self.m = {}
        self.sendLen = totalSendLen
        self.recvMap = {}
        self.recvPos = 0   
        self.iniServerSocks()
        self.peerMaxGot = 0        
        self.iniDsock()
        self.scapySock = conf.L2socket(iface=con_iface)  
        self.notSendNum = 0
        self.sendNum = 0
    def send(self,d):
        lock.acquire()
        sip,dport,seq,ack,window = copy.deepcopy(one_g)
        lock.release()
        if not sip:
            self.notSendNum += 1
            return
        self.sendNum += 1
        ip = IP(dst=sip)
        st = key_g+d
        dk = hashlib.pbkdf2_hmac('md5', st, con_key, 2)
        tcp = Ether()/ip/TCP(sport=port_g,dport=dport,flags='PA',seq=seq,ack=ack,window=window)/(st+dk)
        self.scapySock.send(tcp)
        
        
    def iniDsock(self):
        s = socket.socket()
        n = find_free_port(socket.SOCK_STREAM)
        add = ('127.0.0.1',n)
        s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
        s.bind(add)
        s.listen(1)
        def func():
            d = scapyRec(n)
        self.t = t = threading.Thread(target=func)
        t.setDaemon(True)
        t.start()     
        self.dSock,b = s.accept()
        s.close()  
                 
    def getOneStatus(self):
        k = self.recvMap.keys()
        if not k:
            ma = self.recvPos
        else:        
            ma = max(k)
        co = 0
        b = 0
        ss = b''
        for i in range(self.recvPos,ma+1):
            v = 0
            if i in self.recvMap:
                v = 1
            co+=1
            if co==8:
                b |= v
                ss += bytes([b])
                co=0
                b = 0
            else:
                b |= v 
                b <<= 1
        if co!=0:
            b <<= (7-co)
            ss += bytes([b])
        return struct.pack('Q',self.recvPos)+struct.pack('Q',ma)+ss         
    
    def iniServerSocks(self):
        for i in range(500):      
            self.m[i]={'pysock':i,'id':b'','add':None,'sendList':[],'hasSta':0} 
            
    def getNSendAhead(self,n):
        if n<=0:
            return []
        ss = self.peerMaxGot+1
        l = []
        while ss < self.sendPos and ss in self.sendMap:
            if self.sendMap[ss]['peerGot']:
                ss+=1
                continue
            if self.sendMap[ss]['sendTime']>self.maxAskTime:
                ss+=1
                continue
            l.append(ss)
            self.sendMap[ss]['sendTime'] += 1
            ss +=1
            if len(l)==n:
                break
        return l
    
    def dealRecvPack(self,a):
        r,id,ll,hasSta = self.dealRecv(a)
        if not r:
            return -1
        self.m[id] = {'sendList':ll,'hasSta':hasSta}
        self.lastRecvTime = time.monotonic()
        if len(a)>self.maxGotLen:
            self.maxGotLen = len(a)                
        for jj in ll:
            if jj>self.peerMaxGot:
                self.peerMaxGot = jj
            if jj in self.sendMap:
                self.sendMap[jj]['peerGot']=True
        return hasSta
        
    def work(self):
        self.m = {}
        needSta_g = False
        rl,wl = self.rig()
        rl.append(self.dSock)    
        rl,wl,el = select.select(rl,wl,[],self.calWaitTime()/1000)
        self.dealFd(rl,wl)     
        if self.dSock in rl:
            r = self.dSock.recv(tcpReadLen)
            for i in range(len(r)):
                d = recv_q.get()                                  
                needSta = self.dealRecvPack(d)
                if needSta<0:
                    continue
                if needSta >0:
                    needSta_g = True
        self.reFreshSendMap()      
        if needSta_g:
            sta = self.getOneStatus()
        else:
            sta = b''
        for one in self.m.keys():
            self.makePack(one,sta)
            
        if time.monotonic()-self.staTime>1:
            if time.monotonic()-self.lastRecvTime>120:
                import os
                os._exit(0)                                
            self.staTime = time.monotonic()
            self.maxGotLen = self.maxSenLen = 0     
            print (recv_q.qsize(),self.notSendNum,self.sendNum)
            self.notSendNum = self.sendNum = 0
            
    def makePack(self,fd,sta):
        w = structWrapper()
        si = self.m[fd]['hasSta']
        if si>0:
            sendNum = (self.sendLen-20-len(sta))//(self.oneLen+2)
        else:
            sendNum = (self.sendLen-20)//(self.oneLen+2)
            
        if sendNum>len(self.m[fd]['sendList']):
            sendNum = len(self.m[fd]['sendList'])            
        if sendNum<0:
            sendNum = 0  
        w.writeArr(fd)
        w.writeQ(self.sendPos)
        w.writeWord(self.maxGotLen)
        w.writeWord(self.maxSenLen)
        w.writeByte(sendNum)
        for i in range(sendNum):
            p = self.m[fd]['sendList'][i]
            if p not in self.sendMap or p>=self.sendPos:
                w.writeWord(0)
            else:
                w.writeWord(len(self.sendMap[p]['data']))
                w.writeArr(self.sendMap[p]['data'])
        if si>0:
            w.writeByte(1)
            w.writeWord(len(sta))
            w.writeArr(sta)
        else:
            w.writeByte(0)
        sendNum = (self.sendLen-len(w.data))//(10+self.oneLen)
        l = self.getNSendAhead(sendNum)
        w.writeByte(len(l))
        for i in l:
            w.writeQ(i)
            w.writeWord(len(self.sendMap[i]['data']))
            w.writeArr(self.sendMap[i]['data'])
        self.send(w.data)
        if len(w.data)>self.maxSenLen:
            self.maxSenLen = len(w.data)

    def dealRecv(self,d):
        w = structWrapper(d)
        id = w.readArr(2)
        peerPos = w.readQ()
        if peerPos > self.peerPos:
            l = 0
            for i in range(self.peerPos,peerPos):
                l += len(self.sendMap[i]['data'])
                del self.sendMap[i]
            self.peerPos = peerPos
            self.tellAddBuf(l)
        self.gotRate = w.readFloat()
        self.maxAskTime = self.calAskTime()
        hasData = w.readByte()
        if hasData == 1:
            packNum = w.readQ()
            packLen = w.readWord()
            pack = w.readArr(packLen)
            if packNum>=self.recvPos and packNum not in self.recvMap: 
                self.recvMap[packNum] = pack
                while self.recvPos in self.recvMap:
                    self.wrietBuf += self.recvMap[self.recvPos]
                    del self.recvMap[self.recvPos]
                    self.recvPos += 1
        hasSta = w.readByte()
        getNum = w.readByte()
        if getNum==0:
            return True,id,[],hasSta
        gList = []
        startNum = w.readQ()
        gList.append(startNum)
        for i in range(getNum-1):
            gList.append(startNum+w.readDWord())
        return True,id,gList,hasSta      
        
if __name__ == '__main__':
    import threading
    l = findNPorts(2,socket.SOCK_STREAM)
    def ww():
        a = ser(l[0],l[1],serverPackLen,serverSenLen,
                 con_packEffectTime,con_timeoutInternal,22,b'sdfeff')
        while True:
            a.work()
    t = threading.Thread(target=ww)
    t.setDaemon(True)
    t.start()
    from testconn import testconn
    xx = testconn(l[0],l[1])
    
    
  
                
